// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ProductsVersionsSettingsManager.cs" company="Roman '4ux-nbIx' Novitsky">
//   Copyright (c) Roman '4ux-nbIx' Novitsky 2009
// </copyright>
// <summary>
//   Describes and manipulates the products versions settings manager.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace SettingsManager.ComponentSettingsManagers
{
  #region Namespace Imports

  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Xml;

  using JetBrains.Annotations;
  using JetBrains.Application;
  using JetBrains.UI.Application.PluginSupport;
  using JetBrains.Util;

  #endregion


  /// <summary>
  /// Describes and manipulates the products versions settings manager.
  /// </summary>
  internal sealed class ProductsVersionsSettingsManager : CustomSettingsManager
  {
    #region Constructors and Destructors

    /// <summary>
    /// Initializes a new instance of the <see cref="ProductsVersionsSettingsManager"/> class.
    /// </summary>
    public ProductsVersionsSettingsManager()
      : base("RequiredProductsVersions", "Required Products Versions Settings")
    {
    }

    #endregion


    #region Public Methods

    /// <summary>
    /// Gets the options form.
    /// </summary>
    /// <returns>
    /// The options form.
    /// </returns>
    public override SettingsManagerOptionsForm GetOptionsForm()
    {
      return new ProductsVersionsSettingsManagerOptionsForm();
    }


    /// <summary>
    /// Loads the options.
    /// </summary>
    /// <param name="optionsElement">
    /// The options element.
    /// </param>
    /// <returns>
    /// Returns an instance of <see cref="System.Object"/>.
    /// </returns>
    /// <exception cref="NotSupportedException">
    /// <c>NotSupportedException</c>.
    /// </exception>
    [NotNull]
    public override object LoadOptions(XmlElement optionsElement)
    {
      var requiredProducts = new List<string>();

      foreach (XmlElement element in optionsElement.ChildNodes)
      {
        if (element.Name != "RequiredProduct")
        {
          continue;
        }

        requiredProducts.Add(element.InnerText);
      }

      return requiredProducts;
    }


    public override void LoadSettings(XmlElement element, [CanBeNull] object options)
    {
      if (options == null)
      {
        return;
      }

      var requiredProducts = (List<string>)options;

      if (requiredProducts.Count == 0)
      {
        return;
      }

      var versions = new Dictionary<string, Version>();

      foreach (XmlElement requiredProductNode in element.ChildNodes)
      {
        if (requiredProductNode.Name != "Product")
        {
          continue;
        }

        XmlElement nameElement = requiredProductNode["Name"];
        XmlElement versionElement = requiredProductNode["Version"];

        if (nameElement == null || versionElement == null)
        {
          continue;
        }

        versions.Add(nameElement.InnerText, new Version(versionElement.InnerText));
      }

      IList<Plugin> plugins = PluginManager.Instance.Plugins;

      var missingProducts = new List<string>();
      var outdatedProducts = new List<string>();
      var disabledProducts = new List<string>();

      foreach (Plugin plugin in plugins)
      {
        if (!plugin.Enabled || !plugin.Loaded)
        {
          disabledProducts.Add(plugin.Title);
        }

        Version requiredVersion;
        if (!versions.TryGetValue(plugin.Title, out requiredVersion))
        {
          continue;
        }

        if (requiredVersion > plugin.Version)
        {
          outdatedProducts.Add(plugin.Title);
        }
      }

      bool resharperIsOutdated = false;

      IApplicationDescriptor applicationDescriptor = Shell.Instance.Descriptor;
      if (versions.ContainsKey(applicationDescriptor.ProductName))
      {
        Version requiredResharperVersion = versions[applicationDescriptor.ProductName];

        if (requiredResharperVersion > applicationDescriptor.ProductVersion)
        {
          resharperIsOutdated = true;
        }
      }

      missingProducts.AddRange(
        requiredProducts.Where(
          requiredProduct =>
          !plugins.Where(p => p.Title == requiredProduct).Any() && requiredProduct != applicationDescriptor.ProductName));

      var builder = new StringBuilder();

      if (resharperIsOutdated)
      {
        builder.AppendLine("New ReSharper version available. Please update!");
        builder.AppendLine();
      }

      if (missingProducts.Any())
      {
        builder.AppendLine("The following required ReSharper plug-ins are missing, please install:");

        foreach (string product in missingProducts)
        {
          builder.AppendLine(product);
        }

        builder.AppendLine();
      }

      if (disabledProducts.Any())
      {
        builder.AppendLine("The following required ReSharper plug-ins are disabled, please enable:");

        foreach (string product in disabledProducts)
        {
          builder.AppendLine(product);
        }

        builder.AppendLine();
      }

      if (outdatedProducts.Any())
      {
        builder.AppendLine("The following required ReSharper plug-ins are outdated, please update:");

        foreach (string product in outdatedProducts)
        {
          builder.AppendLine(product);
        }
      }

      if (builder.Length > 0)
      {
        MessageBox.ShowExclamation(builder.ToString(), "Installed Products Check Failed");
      }
    }


    /// <summary>
    /// Saves the options.
    /// </summary>
    /// <param name="optionsElement">
    /// The options element.
    /// </param>
    /// <param name="options">
    /// The options.
    /// </param>
    /// <exception cref="NotSupportedException">
    /// <c>NotSupportedException</c>.
    /// </exception>
    public override void SaveOptions(XmlElement optionsElement, object options)
    {
      var requiredProducts = (List<string>)options;

      foreach (string product in requiredProducts)
      {
        optionsElement.CreateElement("RequiredProduct").InnerText = product;
      }
    }


    public override void SaveSettings(XmlElement element, [CanBeNull] object options)
    {
      var versions = GetProductsVersions();

      List<string> products;

      if (options != null)
      {
        products = (List<string>)options;
      }
      else
      {
        products = new List<string>();
      }

      foreach (KeyValuePair<string, Version> version in versions)
      {
        if (!products.Contains(version.Key))
        {
          continue;
        }

        var xmlElement = element.CreateElement("Product");
        xmlElement.CreateElement("Name").InnerText = version.Key;
        xmlElement.CreateElement("Version").InnerText = version.Value.ToString();
      }
    }

    #endregion


    #region Methods

    [NotNull]
    internal static Dictionary<string, Version> GetProductsVersions()
    {
      var versions = new Dictionary<string, Version>();
      var applicationDescriptor = Shell.Instance.Descriptor;

      versions.Add(applicationDescriptor.ProductName, applicationDescriptor.ProductVersion);

      foreach (Plugin plugin in PluginManager.Instance.Plugins)
      {
        versions.Add(plugin.Title, plugin.Version);
      }

      return versions;
    }

    #endregion
  }
}